
#[derive(Debug)]
struct Event {
    name: String,
}

impl Event { 
    fn new(name: &str) -> Self {
        Self {
            name: name.to_owned(),
        }
    }
    fn get_name(&self) -> &str {
        &self.name
    }
}

#[derive(Debug)]
struct Channel {
    data: Option<Event>,
}

impl Channel {
    fn new() -> Self {
        Self{
            data: None 
        }
    }
    fn push(&mut self, e: Event) {
        self.data.replace(e);
    }
    fn pop(&mut self) -> Option<Event> {
        self.data.take()
    }
}



mod use_parameter {

use crate::src_channel_sink::{Event, Channel};

#[derive(Debug)]
struct Source {
}
impl Source {
    fn new() -> Self {
        Self{}
    }
    fn process(&self, channel: &mut Channel) {
        let e = Event::new("hello");
        channel.push(e);
    }
}


#[derive(Debug)]
struct Sink {
}
impl Sink {
    fn new() -> Self {
        Self {}
    }
    fn process(&self, channel: &mut Channel) {
        let e = channel.pop().unwrap();
        println!("{:?}, name:{:?}", &e, &e.get_name());
    }
}


#[derive(Debug)]
pub struct Agent {
    channel: Channel,
    source: Source,
    sink: Sink,
}

impl Agent {
    pub fn new() -> Self{
        Self {
            channel : Channel::new(),
            source: Source::new(),
            sink: Sink::new(),
        }
    }
    pub fn process(&mut self) {
        self.source.process(&mut self.channel);
        self.sink.process(&mut self.channel)
    }

}

}//mod use_parameter

mod use_ref {

use crate::src_channel_sink::{Event, Channel};
use std::rc::Rc;
use std::cell::RefCell;
use std::fmt;

#[derive(Debug)]
struct Source {
    channel: Rc<RefCell<Channel>>,
}

impl Source {
    fn new(channel: Rc<RefCell<Channel>>) -> Self{
        Self {channel}
    }
    fn process(&self) {
        let e = Event::new("hello");
        self.channel.borrow_mut().push(e);
    }

}



#[derive(Debug)]
struct Sink {
    channel: Rc<RefCell<Channel>>,
}

impl Sink {
    fn new(channel: Rc<RefCell<Channel>>) -> Self {
        Self { channel }
    }
    fn process(&self) {
        let e = self.channel.borrow_mut().pop().unwrap();
        println!("{:?}, name:{:?}", &e, &e.get_name());
    }
}



pub struct Agent {
    channel: Rc<RefCell<Channel>>,
    source: Source,
    sink: Sink,
}

impl fmt::Debug for Agent {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Agent")
            .field("channel", &self.channel.borrow())
            .field("source", &self.source)
            .field("sink", &self.sink)
            .finish()
    }
}

impl Agent {
    pub fn new() -> Self {
        let channel =  Rc::new(RefCell::new(Channel::new()));
        let source  = Source::new(Rc::clone(&channel));
        let sink    = Sink::new(Rc::clone(&channel));
        Self {
            channel,
            source,
            sink,
        }
    }
    pub fn process(&self) {
        self.source.process();
        self.sink.process();
    }
}


}//mod use_ref


//TODO: multi-thread
mod use_thread_safe {

use crate::src_channel_sink::{Event, Channel};
use std::sync::{Arc, Mutex};

#[derive(Debug)]
struct Source {
    channel: Arc<Mutex<Channel>>,
}
impl Source {
    fn new(channel :Arc<Mutex<Channel>>) -> Self {
        Self {channel}
    }
    fn process(&self){
        let e = Event::new("hello");
        self.channel.lock().unwrap().push(e);
    }
}
#[derive(Debug)]
struct Sink {
    channel: Arc<Mutex<Channel>>, 
}
impl Sink {
    fn new(channel :Arc<Mutex<Channel>>) -> Self {
        Self {channel}
    }
    fn process(&self) {
        let e = self.channel.lock().unwrap().pop().unwrap();
        println!("{:?}, name:{:?}", &e, &e.get_name());
    }
}


#[derive(Debug)]
pub struct Agent {
    channel: Arc<Mutex<Channel>>,
    source: Source,
    sink: Sink,
}

impl Agent {
    pub fn new() -> Self {
        let channel = Arc::new(Mutex::new(Channel::new()));
        let source = Source::new(Arc::clone(&channel));
        let sink = Sink::new(Arc::clone(&channel));
        Self {
            channel,
            source, 
            sink,
        }
    }

    pub fn process(&self) {
        self.source.process();
        self.sink.process();
    }
}

}// mode use_thread_safe

//TODO: use unsafe 
mod use_unsafe {
use crate::src_channel_sink::{Event, Channel};

#[derive(Debug)]
struct Source {
    channel: *mut Channel,
}
impl Source {
    fn new(channel: *mut Channel) -> Self {
        Self {channel}
    }
    fn process(&self) {
        let e = Event::new("hello-unsafe");
        
        unsafe {(*self.channel).push(e);}
    }
}
#[derive(Debug)]
struct Sink {
    channel: *mut Channel,
}
impl Sink {
    fn new(channel: *mut Channel) -> Self {
        Self {channel}
    }

    fn process(&self) {
        let e ;
        unsafe {
            e = (*self.channel).pop().unwrap();
        }
        println!("{:?}, name:{:?}", &e, &e.get_name());
    }
}

#[derive(Debug)]
pub struct Agent {
    channel: *mut Channel,
    source: Source,
    sink: Sink,
}

impl Agent {
    pub fn new() -> Self {
        let tmp = Box::new(Channel::new());
        let channel = Box::into_raw(tmp);
        Self {
            channel: channel,
            source: Source::new(channel),
            sink: Sink::new(channel),
        }
    }
    pub fn process(&self) {
        self.source.process();
        self.sink.process();
    }
}


impl Drop for Agent {
    fn drop(&mut self) {
        println!("Unsafe -> drop the Agent");
        unsafe {
            drop(Box::from_raw(self.channel));
        }
    }
}

}//mode use_unsafe


#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn use_channel_with_parameter(){
        let mut a = use_parameter::Agent::new();
        a.process();
    }

    #[test]
    fn use_channel_with_ctor(){
        let a = use_ref::Agent::new();
        a.process();
        println!("{:?}", &a);
    }

    #[test]
    fn use_thread_safe(){
        let a = use_thread_safe::Agent::new();
        a.process();
        println!("{:?}", &a);
    }

    #[test]
    fn use_unsafe(){
        let a = use_unsafe::Agent::new();
        a.process();
        println!("{:?}", &a);
    }
}
